Skip to content

fix(femwell): solve modes at element order 2 by default - #483

Merged
vvahidd merged 4 commits into
gdsfactory:mainfrom
benvial:fix/femwell-order-2-default
Nov 9, 2026
Merged

vvahidd merged 4 commits into
gdsfactory:mainfrom
benvial:fix/femwell-order-2-default

Conversation

@benvial

@benvial benvial commented Nov 6, 2026

Copy link
Copy Markdown
Member

Summary

gsim.femwell.solve_modes now defaults to element order 2, for optical and RF solves. At order 1 the series impedance of a lossy metal line comes out percent low on a mesh that is fine at the conductors and coarse in the dielectric, and refining at the conductors does not fix it. gsim.femwell is not in 0.8.0, so the default changes without a deprecation period. order=1 stays available for cheaper solves.

Evidence

  • New tests/femwell/test_series_resistance.py: a 2 x 0.5 um aluminium strip at 1 GHz against its DC resistance. Order 1 reads 8 % low; order 2 is within 4.3e-4.
  • tests/femwell/test_cross_validation.py now runs femwell at order 2 with N_EFF_RTOL = 1e-3. Against Palace on the same mesh (run on the GDSFactory+ cloud), order 2 agrees to 4.1e-5 (optical) and 2.9e-4 (RF materials); order 1 differed by about 9e-3. This test is palace_local and was not run locally.
  • The TW-MZM notebook now uses the default for both solves.

Tests

Locally: pytest tests/femwell 36 passed, 2 deselected (palace_local).

CI

In-fork CI on this branch: tests on Linux, macOS and Windows, pre-commit and coverage pass. Notebook execution and cloud smoke tests fail there only for lack of the GDSFactory+ API key.

At element order 1, the series impedance of a lossy metal line read from a
femwell mode (gamma * Z0) comes out several percent low on a mesh that is
fine at the conductors and coarse in the dielectric. Refining only near the
conductors does not fix it; order 2 on the same mesh does. solve_modes now
defaults to order=2, so RF line solves, including the TW-MZM notebook's,
get the accurate series side without extra arguments.

gsim.femwell first appeared after 0.8.0 (gdsfactory#298) and has not been released,
so the default changes without a deprecation period.

The notebook's optical solve now passes order=1, so its per-bias cost and
results stay as they were. Two tests that do not depend on the element
order also pin order=1: the staircase convergence test, whose five solves
on one mesh got about seven times slower, and the Palace cross-validation
test, whose tolerance was set for order 1.

A regression test solves an aluminium strip in a PEC box at 1 GHz and
checks that its series resistance matches the strip's DC resistance to
3e-3. At order 1 the same mesh reads about 8 % low.
The TW-MZM notebook's optical solve no longer pins order=1, so it takes the
solve_modes default like the RF solve. The order parameter's docstring now
describes order 2 as the general default rather than an RF-only choice.

Two tests keep order=1 because their own checks depend on it, and each
pin now says why. The staircase convergence test compares materials on
one fixed mesh, so the order cancels out. The Palace cross-validation
tolerance was set for femwell at order 1.
The Palace cross-validation test now solves femwell at the solve_modes
default, order 2, instead of pinning order 1, and its n_eff tolerance is
tightened from 1e-2 to 1e-3.

On the test's mesh, femwell at order 2 agrees with Palace to 4.1e-5
relative on Re(n_eff) for the optical case and to 2.9e-4 on the complex
n_eff for the RF materials. At order 1 both cases sit near 9e-3, just
inside the old bound. The new bound keeps a margin of more than three
over the measured agreement and rejects the order-1 result.

These numbers were checked against Palace on the GDSFactory+ cloud on
the same mesh. The test is marked palace_local and was not run locally.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-06T16:51:38.679922Z ca55e7a New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added the bug Something isn't working label Nov 6, 2026
@codecov

codecov Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.14%. Comparing base (241145c) to head (ca55e7a).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #483      +/-   ##
==========================================
- Coverage   91.16%   91.14%   -0.02%     
==========================================
  Files         181      182       +1     
  Lines       25940    26119     +179     
  Branches     4897     4939      +42     
==========================================
+ Hits        23647    23805     +158     
- Misses       1317     1331      +14     
- Partials      976      983       +7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Alisama20 Alisama20 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran the femwell tests (36 passed, Palace test skipped) and tests/common and tests/tcad (1205 passed). On a 500x220 nm Si strip against an h=0.01 order-2 reference, order 2 is better at equal cost. Order 1 at 6196 elements takes 1.0 s with an error of 1.6e-3. Order 2 at 1064 elements takes 0.58 s with an error of 6e-5. The 8 % and 4.3e-4 series-resistance numbers reproduce on the test mesh.
The "a few times more per solve" in the docstring is low. I measured 2.5x on a 94-element mesh and 8x to 12x above 1000 elements.
The new test docstring says refining at the strip does not move order 1. It does: -8.1 % goes to -12.6 % at 0.025. Order 2 at 0.025 reads -3.2e-3, just outside your 3e-3 bound, so the fix is partial.
The TW-MZM notebook uses the new default but the prose (9.7 GHz, 7.9, 12.7 and 17.6 dB/cm) is order 1. Please re-run it or say it is left out. I did not run it.

Correct two claims from review. Refining at the strip does move the
order 1 reading (8.0 % low at 50 nm, 12.6 % low at 25 nm) and leaves
order 2 3.2e-3 low at 25 nm: the remaining error comes from the coarse
oxide, which a 0.5 um cap removes (order 2 at 7e-5). Say so in the
solve_modes docstring and the test module, and replace the per-solve
cost estimate with the measured range (about 2.5x on a hundred elements,
about 10x above a thousand).
@benvial

benvial commented Nov 6, 2026

Copy link
Copy Markdown
Member Author

Thanks @Alisama20 for the measurements. You were right on both docstrings, fixed in ca55e7a.

  • Cost: the order docstring now gives your measured range, about 2.5x on a mesh of a hundred elements and about 10x above a thousand.
  • Refinement: I reproduced your numbers on the test geometry. Order 1 goes from 8.0 % low at 50 nm to 12.6 % low at 25 nm, and order 2 reads 3.2e-3 low at 25 nm. The remaining error is in the coarse 2 µm oxide, not at the strip: capping the oxide at 0.5 µm brings order 1 to 1.0 % low and order 2 to 7e-5. So order 2 reduces this error by one to two orders of magnitude on a coarse far mesh but does not remove it. The test docstring now says that, and the solve_modes docstring says to cap the far element size when the series impedance must be accurate to 1e-4. The test still checks the 50 nm / 2 µm mesh at 3e-3, which order 2 meets and order 1 does not.
  • Notebook: I have left the TW-MZM notebook out of this PR and did not rerun it, so its prose (9.7 GHz, 7.9, 12.7 and 17.6 dB/cm) still reflects order 1. It needs a rerun before or right after this lands.

One open point: an order-2 solve at 25 nm strip and 0.5 µm oxide (7,906 elements) returned a wildly wrong R' (relative error -2e7). It looks like the wrong mode was picked, but I have not confirmed that yet and will look into it.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca55e7aa28

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

wavelength_um: float,
num_modes: int = 1,
order: int = 1,
order: int = 2,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Regenerate the TW-MZM notebook for the new default

When the published nbs/tcad_twmzm.ipynb workflow runs, its optical solve (lines 751-756) and RF sweep (lines 993-1000) both omit order, so this default change moves them from order 1 to order 2. The notebook itself is unchanged from the parent and still states the old fixed RF-index/impedance range and loss/bandwidth figures at lines 1036-1044 and 1286-1291; these are downstream of the series impedance that this change says is percent-low at order 1, so the generated values and explanatory text can no longer be treated as the same experiment. Re-execute and update the numerical narrative, or explicitly retain order 1 there.

AGENTS.md reference: AGENTS.md:L54-L56

Useful? React with 👍 / 👎.

@Alisama20 Alisama20 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not reproduce the -2e7 with 4 modes at 25 nm / 0.5 µm. The line mode is picked there and R′ reads -2.0e-4 at order 2.
It appears when more modes are requested. At 8 to 16 modes select_line_mode returns a spurious metal mode with |n_eff| of 1e4 to 5e4, at order 2 and also at order 1. These pass propagating_modes and win on the largest Re(n_eff), and R′ is then off by about 1e9.
So the cause is mode selection, not order 2. It needs its own issue.
The 1e-4 in the solve_modes docstring is optimistic. R′ moves by 2e-4 to 8e-4 with n_guess alone on the 25 nm / 0.5 µm mesh at order 2.

@Alisama20 Alisama20 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order-2 default is fine. tcad_twmzm and tcad_pn_junction run to the end on this branch, and the femwell tests pass (36).
The spurious-mode selection is separate from this PR.

@vvahidd
vvahidd merged commit 25c202d into gdsfactory:main Nov 9, 2026
19 checks passed
@benvial

benvial commented Nov 11, 2026

Copy link
Copy Markdown
Member Author

Follow-up on the open point above: the order-2 solve at 25 nm strip / 0.5 µm oxide that gave R′ off by about -2e7 was a wrong mode pick. Asked for more modes, the solve also returns modes confined to the metal with |n_eff| near |sqrt(eps_metal)|, and select_line_mode took one of them. @Alisama20 tracked it down in #498; the fix is in #509 (max_index=conductor_index_bound(eps)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants